﻿using System;
using System.Windows.Forms;
using KLTP2UI.Properties;

namespace KLTP2UI
{
	public partial class MainForm : Form
	{
		private uint _tIdx;
		private ulong _searchPos;
		private ParseController _controller;
		private AI _ai;
		private Filter _filter;

		public MainForm()
		{
			InitializeComponent();
			_filter = new Filter();
		}

		private void Form1Load(object sender, EventArgs e)
		{
			DllMem.MmInitialize();
		}
		
		private void Form1FormClosing(object sender, FormClosingEventArgs e)
		{
			StopBackGroudThreads();
			DllMem.MmDeInitialize();
		}

		private void StopBackGroudThreads()
		{
			if(_controller != null)
				_controller.StopProcessing();

			if(_ai != null)
				_ai.Stop();
		}

		//--------------------------------------------

		private void Button1Click(object sender, EventArgs e)
		{
			if(_controller!=null && _controller.IsProcessing())
			{
				button1.Enabled = false;
				_controller.StopProcessing();
				return;
			}

			if (openFileDialog1.ShowDialog() != DialogResult.OK) 
				return;

			StopBackGroudThreads();

			textBox2.Text = openFileDialog1.FileName;
			listBox1.Clear();
			dataGridView1.RowCount = 0;
			filterCheckBox.Checked = false;
			_filter = new Filter();
			
			button1.Enabled = false;
			DllMem.MmReInitialize();
			button1.Enabled = true;

			button1.Text = Resources.F1B1Cancel;
			progressBar1.Visible = true;
			progressBar1.Value = 0;
			EnableBottomControls(false);

			_controller = new ParseController(openFileDialog1.FileName);
			_controller.SomeProgress += ControllerSomeProgress;
			_controller.StartProcessing();
		}

		private void ControllerSomeProgress(object sender, EventArgs e)
		{
			if (InvokeRequired)
			{
				BeginInvoke((EventHandler)ControllerSomeProgress, new []{sender, e});
				return;
			}

			var controller = sender as ParseController;
			if(controller == null)
				return;

			if(controller.IsProcessing())
			{
				progressBar1.Maximum = (int)(Math.Ceiling(controller.Length / 10.0));
				progressBar1.Value = (int)(Math.Ceiling(controller.Processed / 10.0));
			}
			else
			{
				button1.Text = Resources.F1B1Load;
				button1.Enabled = true;
				for (uint tIdx = 0; tIdx < DllMem.GetThreadsCountTotal(); tIdx++)
					listBox1.AddVisual(new VisualThread(DllMem.GetThreadIdForIndex(tIdx)));
				progressBar1.Visible = false;
				EnableBottomControls(true);
#if DEBUG
				_ai = new AI();
				_ai.ThreadRenamePending += RenamePendingHandler;
				_ai.Run();
#endif
			}
		}

		private void ListBox1SelectedIndexChanged(object sender, EventArgs e)
		{
			LoadThreadToGrid(false);
		}

		private void LoadThreadToGrid(bool justFastReload)
		{
			dataGridView1.RowCount = 0;
			if (!justFastReload)
			{
				if (listBox1.SelectedIndex == -1)
					return;
				_tIdx = (uint) (listBox1.SelectedVisualIndex);
			}
			if (_filter.Enabled)
				DllMem.FilterApply(_tIdx);
			else
				DllMem.FilterClear();
			if (listBox1.SelectedIndex > -1)
				dataGridView1.RowCount = (int) DllMem.GetThreadEventsCount(_tIdx, true);
		}

		private void DataGridView1CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
		{
			var column = dataGridView1.Columns[e.ColumnIndex];
			if (ReferenceEquals(column, colType))
			{
				var typeId = DllMem.ALGetEventType(_tIdx, (uint)e.RowIndex, true);
				e.Value = typeId != null ? imageList1.Images[(int)typeId] : null;
				//See CMemMan::GetTypeFast() for details; these are bound to grid images
			}
			else if (ReferenceEquals(column, colTime))
				e.Value = DllMem.ALGetEventTime(_tIdx, (uint)e.RowIndex, true);
			else if (ReferenceEquals(column, colSrv))
				e.Value = DllMem.ALGetEventService(_tIdx, (uint)e.RowIndex, true);
			else if (ReferenceEquals(column, colMsg))
				e.Value = DllMem.ALGetEventMessage(_tIdx, (uint)e.RowIndex, true).Replace("\t", "    ");
			else
				e.Value = null;
		}

		//----------------------------------------------

		private void PerformSearch(object sender, EventArgs e)
		{
			//Part 1: set up direction and shifting
			var shift = false;  //Do we need mandatory +/-1 event shift when starting search - for Up/Down keys
			var direction = 1;  //Down
			if (e is KeyEventArgs)
			{
				var f = e as KeyEventArgs;
				if (f.KeyCode == Keys.Up || f.KeyCode == Keys.Down)
				{
					f.SuppressKeyPress = true;
					shift = true;
					if (f.KeyCode == Keys.Up)
						direction = -1;
				}
				else
					return;     //No other keys should be processed this way except up and down arrows
			}
			EnableBottomControls(false);
			Application.DoEvents();

			//Part 2: let's search it
			int tIdx = 0, eIdx = 0, firstBlockedCount = 0, firstBlockedThread = -1, firstBlockedEvent = -1;
			bool threadIsVisible = true;                     //Is found by searcher thread visible
			do
			{
				_searchPos = DllMem.TermSearch(_searchPos, direction, textBox1.Text.ToLower(), shift);
				if (_searchPos != 0xFFFFFFFFFFFFFFFF)
				{
					tIdx = (int) (_searchPos >> 32);
					eIdx = (int) (_searchPos & 0xFFFFFFFF);
					shift = true; //If current thread is hidden, we're going to shift initial eIdx to _continue_ search
					threadIsVisible = listBox1.GetVisual(tIdx).Visible;
					if (firstBlockedThread == -1 && !threadIsVisible) //If we first time got hit inside invisible thread
					{
						firstBlockedThread = tIdx; //we'll remember it
						firstBlockedEvent = eIdx; //and remember the index of event inside it - for if the next hit 
					} //will be in same thread we don't think we'we done round
					if (firstBlockedThread == tIdx && firstBlockedEvent == eIdx)
						//We assume thread is still hidden during search session
						firstBlockedCount++; //let's remember we've been here already
				}
			} while (_searchPos != 0xFFFFFFFFFFFFFFFF && !threadIsVisible && firstBlockedCount < 2);
			//i.e. we'll exit on:
			//1) 0xFFFFFFFFFFFFFFFF - TermNotFound exit code of TermSearch;
			//2) if currently found thread is visible;
			//3) if currently found thread is invisible and we've already seen current event inside it.

			//Part 3: let's show user the results
			EnableBottomControls(true);
			textBox1.Focus();
			if (_searchPos != 0xFFFFFFFFFFFFFFFF && threadIsVisible)
			{
				tIdx = listBox1.Visual2Item4Index(tIdx);            //If some threads above are hidden
				listBox1.SelectedIndex = tIdx;
				dataGridView1.ClearSelection();
				dataGridView1.Rows[eIdx].Selected = true;
				dataGridView1.FirstDisplayedScrollingRowIndex = Math.Max(0, eIdx - (dataGridView1.DisplayedRowCount(false)/2));
			}
			else
			{
				MessageBox.Show(Resources.Search_NoMoreOccurences);
				_searchPos = 0;
			}
		}

		private void EnableBottomControls(bool enable)
		{
			listBox1.Enabled = enable;
			textBox1.Enabled = enable;
			filterCheckBox.Enabled = enable;
			editFilterLinkLabel.Enabled = enable;
		}

		//----------------------------------------------

		private void ContextMenuStrip1Opening(object sender, System.ComponentModel.CancelEventArgs e)
		{
			hideToolStripMenuItem.Enabled = (listBox1.SelectedIndex > -1);
			renameToolStripMenuItem.Enabled = hideToolStripMenuItem.Enabled;
		}

		private void HideToolStripMenuItemClick(object sender, EventArgs e)
		{
			listBox1.SelectedVisual.Visible = false;
		}

		private void ShowAllToolStripMenuItemClick(object sender, EventArgs e)
		{
			for (var i = 0; i < listBox1.VisualCount(); i++)
			{
				listBox1.GetVisual(i).Visible = true;
			}
		}

		private void RenameToolStripMenuItemClick(object sender, EventArgs e)
		{
			var box = new Renamer();
			box.Text += listBox1.SelectedVisual.TidHex;
			box.ShowDialog(this);
			if (box.DialogResult == DialogResult.OK)
				RenamePendingHandler(sender, new AI.ThreadRenamePendingEventArgs(listBox1.SelectedVisualIndex, 
																				 box.textBox1.Text));
		}

		private void RenamePendingHandler (object sender, AI.ThreadRenamePendingEventArgs e)
		{
			if (InvokeRequired)
				Invoke(new Action<object, AI.ThreadRenamePendingEventArgs>(RenamePendingHandler), new[] {sender, e});
			else
				listBox1.GetVisual(e.ThreadIdx).FriendlyName = e.Name;
		}
		//----------------------------------------------

		private void FilterCheckBoxCheckedChanged(object sender, EventArgs e)
		{
			if (filterCheckBox.Checked && _filter.IsDefault)
			{
				var filterEd = new FilterEditor(_filter);
				filterEd.ShowDialog(this);
				if(filterEd.DialogResult == DialogResult.Cancel || filterEd.Result.IsDefault)
				{
					filterCheckBox.Checked = false;	
					return;
				}
				_filter = filterEd.Result; 
			}
			ActivateFilter();
		}

		private void EditFilterLinkLabelLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			var filterEd = new FilterEditor(_filter);
			filterEd.ShowDialog(this);
			if (filterEd.DialogResult == DialogResult.OK)
			{
				_filter = filterEd.Result;
				if (filterCheckBox.Checked == _filter.IsDefault)
					filterCheckBox.Checked ^= true;
				else if (!_filter.IsDefault)
					ActivateFilter();
			}
		}

		private void ActivateFilter()
		{
			_filter.Enabled = filterCheckBox.Checked;
			_filter.RegisterToDllMem();
			LoadThreadToGrid(true);
		}

	}
}